﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace XinChen
{
    /// <summary>
    /// 软件锁管理类，提供访问锁，锁信息读取，自动定时检查等
    /// </summary>
    class UKey
    {
        private static int instance = 0;//计算实例化的总数量

        #region 实时读取软件锁的各个预设数据区。

        /// <summary>
        /// 是否已经初始化成功，
        /// 读取属性时，如果失败，Ready也会变为False。属性会返回“ErrCode:错误码”
        /// </summary>
        public bool Ready { get; protected set; }

        /// <summary>
        /// 操作发生错误后，由操作函数提供的错误信息
        /// </summary>
        public string LastErrMsg { get; protected set; }

        /// <summary>
        /// 软件锁的全球唯一序号，
        /// 如果获取失败，会设置为“ErrCode:错误码”，Ready变为False
        /// </summary>
        public string HardwareID
        {
            get
            {
                if (!Ready) { return "ErrCode:NoReady!"; }
                StringBuilder HWID = new StringBuilder(32);
                Ready = false;
                try
                {
                    int result = UK7.NTGetHardwareID(HWID);
                    if (result != 0)
                    {
                        return "ErrCode:" + result.ToString();
                    }
                    else
                    {
                        Ready = true;
                        return HWID.ToString();
                    }
                }
                catch
                {
                    return "ErrCode:Unknow!";
                }
            }
        }

        /// <summary>
        /// 设置应用程序标记字串，用于核对"数据区1"的信息。
        /// </summary>
        public string DZ1_VerifyString;
        /// <summary>
        /// 数据区1的信息，应用程序标记字符串。0(32)
        /// 如果获取失败，会设置为“ErrCode:错误码”，Ready变为False
        /// </summary>
        public string DataZone1
        {
            get
            {
                if (!Ready) { return "ErrCode:NoReady!"; }
                byte[] buf = new byte[32];
                Ready = false;
                try
                {
                    int result = UK7.NTRead(0, 32, buf);
                    if (result != 0)
                    {
                        return "ErrCode:" + result.ToString();
                    }
                    else
                    {
                        Ready = true;
                        return System.Text.Encoding.Unicode.GetString(buf).TrimEnd('\0');
                    }
                }
                catch
                {
                    return "ErrCode:Unknow!";
                }
            }
            set
            {
                if (!Ready) { return; }
                byte[] buf = Tools.str2bin(value);
                Array.Resize(ref buf, 32);
                try
                {
                    int result = UK7.NTWrite(0, 32, buf);
                    if (result != 0) { LastErrMsg = "写入DataZone1失败！ ErrCode:" + result.ToString(); }
                }
                catch
                {
                    LastErrMsg = "写入DataZone1失败！ ErrCode::Unknow!";
                }
            }
        }

        /// <summary>
        /// 数据区2的信息，按位设置功能控制。64(8)
        /// 如果获取失败，会设置为“ErrCode:错误码”，Ready变为False
        /// </summary>
        public byte[] DataZone2
        {
            get
            {
                if (!Ready) { return null; }
                Ready = false;
                byte[] buf = new byte[8];
                try
                {
                    int result = UK7.NTRead(64, 8, buf);
                    if (result == 0)
                    {
                        Ready = true;
                        return buf;
                    }
                }
                catch
                {
                }
                return null;
            }
        }

        /// <summary>
        /// 数据区3的信息，备注。128(256)
        /// 如果获取失败，会设置为“ErrCode:错误码”，Ready变为False
        /// </summary>
        public string DataZone3
        {
            get
            {
                if (!Ready) { return "ErrCode:NoReady!"; }
                byte[] buf = new byte[256];
                Ready = false;
                try
                {
                    int result = UK7.NTRead(128, 256, buf);
                    if (result != 0)
                    {
                        return "ErrCode:" + result.ToString();
                    }
                    else
                    {
                        Ready = true;
                        return System.Text.Encoding.Unicode.GetString(buf).TrimEnd('\0');
                    }
                }
                catch
                {
                    return "ErrCode:Unknow!";
                }
            }
        }

        /// <summary>
        /// 数据区4的信息，用于渠道跟踪.512(256)
        /// 如果获取失败，会设置为“ErrCode:错误码”，Ready变为False
        /// </summary>
        public string DataZone4
        {
            get
            {
                if (!Ready) { return "ErrCode:NoReady!"; }
                byte[] buf = new byte[256];
                Ready = false;
                try
                {
                    int result = UK7.NTRead(512, 256, buf);
                    if (result != 0)
                    {
                        return "ErrCode:" + result.ToString();
                    }
                    else
                    {
                        Ready = true;
                        return System.Text.Encoding.Unicode.GetString(buf).TrimEnd('\0');
                    }
                }
                catch
                {
                    return "ErrCode:Unknow!";
                }
            }
        }

        #endregion

        private string LockID = "";
        private string UserPass = "";
        /// <summary>
        /// 初始化，成功后属性Ready=True
        /// </summary>
        /// <param name="ProgID">软件识别码</param>
        /// <param name="Password">用户登录密码</param>
        public UKey(string ProgID, string Password)
        {
            Ready = false;
            //控制不能超过1个实例，防止不小心重复打开一个锁
            UKey.instance++;

            //开始初始化
            Open(ProgID, Password);
        }

        /// <summary>
        /// 用之前设置过的软件识别码和密码重新打开锁
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            return Open(LockID, UserPass);
        }

        /// <summary>
        /// 打开指定的加密锁
        /// </summary>
        /// <param name="ProgID">软件识别码</param>
        /// <param name="Password">用户登录密码</param>
        /// <returns></returns>
        public bool Open(string ProgID, string Password)
        {
            LockID = ProgID;
            UserPass = Password;

            //控制不能超过1个实例，防止不小心重复打开一个锁
            if (UKey.instance > 1)
            {
                LastErrMsg = "禁止重复访问锁！";
                return false;
            }
            if (Ready) { UK7.NTLogout(); }
            Ready = false;

            //开始初始化
            try
            {
                //查找加密锁
                int result = UK7.NTFindFirst(LockID);
                if (result != 0)
                {
                    LastErrMsg = "找不到符合的加密锁！（" + result.ToString() + "）";
                    return Ready;
                }

                //打开以便读取信息
                result = UK7.NTLogin(UserPass);
                if (result != 0)
                {
                    LastErrMsg = "打开加密锁失败，请联系客服！（" + result.ToString() + "）";
                    return Ready;
                }
            }
            catch
            {
                LastErrMsg = "无法访问加密锁，请联系客服！";
                return Ready;
            }

            Ready = true;
            return Ready;
        }

        /// <summary>
        /// 析构，计数器减一，锁Logout()。
        /// </summary>
        ~UKey()
        {
            UKey.instance--;
            try
            {
                //释放加密锁
                UK7.NTLogout();
            }
            catch
            {
            }
        }

        /// <summary>
        /// 写入DataZone2，用于软件功能升级。
        /// </summary>
        /// <param name="buf">功能位开关，第一个字节的低位为0，高位为7，第二个字节低位为8，高位为15，以此类推，最高位是63。</param>
        /// <returns>是否成功</returns>
        public bool DZ2_Write(byte[] buf)
        {
            if (!Ready) { return false; }
            try
            {
                if (buf.Length > 8) { return false; }
                int result = UK7.NTWrite(64, buf.Length, buf);
                if (result != 0)
                {
                    Tools.show_err("功能升级失败，请联系客服！（" + result.ToString() + "）");
                    return false;
                }
                return true;
            }
            catch
            {
                Tools.show_err("功能升级失败，请联系客服！");
                return false;
            }
        }

        #region 软件锁检查方法

        /// <summary>
        /// 检查DataZone2的特定位是否为1，第一个字节的低位为0，高位为7，第二个字节低位为8，高位为15，以此类推，最高位是63。
        /// </summary>
        /// <param name="pos">需要检查的位，第一个字节的低位为0，高位为7，第二个字节低位为8，高位为15，以此类推，最高位是63。</param>
        /// <returns>指定位是否为1</returns>
        public bool DZ2_check(int pos)
        {
            if (!Ready) { return false; }

            if (pos >= 64) { return false; }

            byte[] _DZ2 = DataZone2;
            if (_DZ2 != null)
            {
                return (_DZ2[pos / 8] & (0x1 << (pos % 8))) > 0;
            }
            return false;
        }

        //checktimes设最大值，保证第一次调用即产生实际检查动作
        private int checktimes = int.MaxValue - 1;
        /// <summary>
        /// 设定Check()被调用多少次后实际检查加密锁。设为0则每次都检查。
        /// </summary>
        public int CheckRate = 0;
        /// <summary>
        /// 按CheckRate设定的周期调用Check()
        /// </summary>
        /// <returns>未到设定周期或检查成功时返回True</returns>
        public bool Check_byRate() 
        {
            checktimes++;
            if (CheckRate > checktimes)//未到检查周期点
            {
                return true;
            }
            checktimes = 0;

            return Check();
        }
        /// <summary>
        /// 打开锁，检查DataZone1，是否与实际读出的锁信息相同。
        /// </summary>
        /// <returns></returns>
        public bool Check()
        {
            //开始实际检查
            if (Open() && DZ1_VerifyString == DataZone1)
            {
                return true;
            }
            else
            {
                LastErrMsg = "加密锁核对失败！";
                return false;
            }
        }

        /// <summary>
        /// 软件锁出错事件，定时检查时发现软件锁不符时引发事件
        /// </summary>
        public event EventHandler TimerCheck_Error;
        /// <summary>
        /// 用定时器定期检查软件锁
        /// </summary>
        private System.Timers.Timer timer_Check;
        /// <summary>
        /// 开始定时检查软件锁
        /// </summary>
        /// <param name="minute">检查周期（分钟）</param>
        public void TimerCheck_Start(int minute)
        {
            if (timer_Check == null)
            {
                timer_Check = new System.Timers.Timer();
                timer_Check.Elapsed += new System.Timers.ElapsedEventHandler(timer_Check_Elapsed);
            }
            timer_Check.AutoReset = true;
            timer_Check.Interval = minute * 60000;
            timer_Check.Start();
        }
        /// <summary>
        /// 停止定时检查软件锁
        /// </summary>
        public void TimerCheck_Stop()
        {
            timer_Check.Stop();
        }
        //定时器触发，如果设置了事件处理，就引发检查和事件
        void timer_Check_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (TimerCheck_Error != null && !Check())
            {
                LastErrMsg = "加密锁检查失败！";
                TimerCheck_Error(this, new EventArgs());
            }
        }

        #endregion

        /// <summary>
        /// 磐石NT77访问类，http://www.panshitech.com/Download/NT77.htm
        /// </summary>
        private class UK7
        {
            //查找加密锁
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTFindFirst(string NTCode);

            //查询硬件ID
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTGetHardwareID(StringBuilder hardwareID);

            //登录加密锁
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTLogin(string NTpassword);

            //存储区数据读取
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTRead(int address, int Length, byte[] pBuffer);

            //存储区数据写入
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTWrite(int address, int Length, byte[] pBuffer);

            //登出加密锁
            [DllImport("Lib\\UK7.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern int NTLogout();
        }
    }

    /// <summary>
    /// 用户信息基本类，完成信息检查和串行化。
    /// </summary>
    class UserInfo
    {
        public string Name = "";
        public string Email = "";
        public string Phone = "";
        public string Fax = "";
        public string Address = "";
        public string QQ = "";
        public string MSN = "";

        public string HardwareID = "";
        public string SoftwareID = "";
        public byte[] Function = new byte[8];
        public string Notes = "";
        public string Seller = "";

        /// 完整加密流程：
        /// Tools完成：
        /// 1、串行化所有属性，生成二进制数组
        /// 2、加入4字节CRC32校验码
        /// 3、XOR
        /// UserInfo完成：
        /// 4、zip压缩上述数据（其实是扩展）
        /// 5、再次XOR，隐藏zip流的标示。

        /// <summary>
        /// 串行化用户信息并混淆加密
        /// </summary>
        public byte[] ToBin()
        {
            List<byte[]> RegInfo = new List<byte[]>();
            RegInfo.Add(Tools.str2bin(HardwareID));
            RegInfo.Add(Tools.str2bin(SoftwareID));
            RegInfo.Add(Function);
            RegInfo.Add(Tools.str2bin(Notes));
            RegInfo.Add(Tools.str2bin(Seller));

            RegInfo.Add(Tools.str2bin(Name));
            RegInfo.Add(Tools.str2bin(Email));
            RegInfo.Add(Tools.str2bin(Phone));
            RegInfo.Add(Tools.str2bin(Fax));
            RegInfo.Add(Tools.str2bin(Address));
            RegInfo.Add(Tools.str2bin(QQ));
            RegInfo.Add(Tools.str2bin(MSN));

            //串行化并Zip，最后混淆
            return Tools.XOR(Tools.Zip(Tools.list2bin_encrypt(RegInfo)));
        }
        /// <summary>
        /// 保存当前软件锁信息，用于数据上传
        /// </summary>
        /// <param name="myKey">传入UKey类</param>
        /// <returns>成功登记软件锁</returns>
        public bool SaveUKey(UKey myKey)
        {
            if (!myKey.Ready) { return false; }

            HardwareID = myKey.HardwareID;
            SoftwareID = myKey.DataZone1;
            Function = myKey.DataZone2;
            Notes = myKey.DataZone3;
            Seller = myKey.DataZone4;

            return myKey.Ready;
        }

        /// <summary>
        /// 解析（反串行化），用于服务器返回信息
        /// </summary>
        public bool FromBin(byte[] RegData)
        {
            //反混淆，UnZip，交Tools解码。
            List<byte[]> RegInfo = Tools.bin2list_decrypt(Tools.UnZip(Tools.XOR(RegData)));
            if (RegInfo == null) { return false; }

            HardwareID = Tools.bin2str(RegInfo[0]);
            SoftwareID = Tools.bin2str(RegInfo[1]);
            Function = RegInfo[2];
            Notes = Tools.bin2str(RegInfo[3]);
            Seller = Tools.bin2str(RegInfo[4]);

            Name = Tools.bin2str(RegInfo[5]);
            Email = Tools.bin2str(RegInfo[6]);
            Phone = Tools.bin2str(RegInfo[7]);
            Fax = Tools.bin2str(RegInfo[8]);
            Address = Tools.bin2str(RegInfo[9]);
            QQ = Tools.bin2str(RegInfo[10]);
            MSN = Tools.bin2str(RegInfo[11]);

            return true;
        }
    }
}
